//*********************************************************
//
//    Copyright (c) Microsoft. All rights reserved.
//    This code is licensed under the Apache License, Version 2.0.
//    THIS CODE IS PROVIDED *AS IS* WITHOUT WARRANTY OF
//    ANY KIND, EITHER EXPRESS OR IMPLIED, INCLUDING ANY
//    IMPLIED WARRANTIES OF FITNESS FOR A PARTICULAR
//    PURPOSE, MERCHANTABILITY, OR NON-INFRINGEMENT.
//
//*********************************************************

namespace Microsoft.Research.ScientificWorkflow.ManagementStudioControls.RegistryManagerModule
{
    using System.ComponentModel;
    using System.Windows;
    using Microsoft.Research.ScientificWorkflow.ManagementStudioModels;
    using Microsoft.Research.ScientificWorkflow.UIDesigner.WpfComposite;
    using Microsoft.Practices.Unity;
    using ManagementStudioServices.Interfaces;
    using System.Text;
    using Microsoft.Research.ScientificWorkflow.TridentUtilities;
    using Microsoft.Research.ScientificWorkflow.ManagementStudioControls.Common;
    using SR = Microsoft.Research.DataLayer;
    using System;
    using System.Linq;
    using Microsoft.Win32;
    using System.Collections.ObjectModel;
    using System.Windows.Controls;
    using System.Windows.Input;
    using System.Collections;
    using Microsoft.Research.ScientificWorkflow.UIDesigner;

    /// <summary>
    /// The presenter for the type initializer item view.
    /// </summary>
    public class TypeInitializerCenterPanePresenter : IRegistryManagerCenterPanePresenter, INotifyPropertyChanged
    {
        #region Private Members

        /// <summary>
        /// The unity container.
        /// </summary>
        private IUnityContainer container;

        /// <summary>
        /// Service for registry access.
        /// </summary>
        private IRegistryManagerService registryManagerService;

        /// <summary>
        /// The current model.
        /// </summary>
        private TypeInitializerModel currentModel;

        /// <summary>
        /// The current author.
        /// </summary>
        private string currentAuthor;

        /// <summary>
        /// Flag specifying if the state has changed.
        /// </summary>
        private bool stateChanged;

        /// <summary>
        /// A control that indicates that there is some work happening in the background.
        /// </summary>
        private WaitControl waitControl;

        #endregion Private Members

        #region Constructor

        /// <summary>
        /// Initializes a new instance of the <see cref="TypeInitializerCenterPanePresenter"/> class.
        /// </summary>
        /// <param name="container">The container.</param>
        /// <param name="contentView">The content view.</param>
        /// <param name="service">The service.</param>
        public TypeInitializerCenterPanePresenter(IUnityContainer container, TypeInitializerCenterPane contentView, IRegistryManagerService service)
        {
            this.container = container;
            this.ContentView = contentView;
            this.registryManagerService = service;
            contentView.DataContext = this;

            this.SaveChangesCommand = new DelegateCommand<SaveStateHandler>(this.SaveChangesExecute);
            this.CancelChangesCommand = new DelegateCommand<object>(p => this.CancelChangesExecute());
            this.ImportCommand = new DelegateCommand<object>(p => this.ImportCommandExecute());
            this.DeleteTypeInitializerCommand = new DelegateCommand<object>(p => this.DeleteTypeInitializerCommandExecute());
        }

        #endregion Constructor

        #region Properties

        /// <summary>
        /// Gets or sets the current model.
        /// </summary>
        /// <value>The current model.</value>
        public TypeInitializerModel CurrentModel
        {
            get
            {
                return this.currentModel;
            }
            set
            {
                this.currentModel = value;
                this.CurrentAuthor = (this.currentModel == null) ? string.Empty : this.currentModel.Author;
                this.stateChanged = false;

                try
                {
                    if (this.currentModel != null && null == this.currentModel.RelatedWorkflows)
                    {
                        this.currentModel.RelatedWorkflows = new ObservableCollection<WorkflowViewModel>(this.registryManagerService.FetchRelatedWorkflows(this.currentModel));
                    }
                }
                catch (TridentCustomException ex)
                {
                    TridentMessageBox.ShowTridentErrorMessageBox(ex.Message);
                }

                this.container.Resolve<IStatusMessageControl>().SetText(string.Empty);
                this.RaisePropertyChanged("CurrentModel");
            }
        }

        /// <summary>
        /// Gets or sets the current author.
        /// </summary>
        /// <value>The current author.</value>
        public string CurrentAuthor
        {
            get
            {
                return this.currentAuthor;
            }
            set
            {
                string author = value == null ? string.Empty : value;
                this.currentAuthor = string.Empty;
                if (!string.IsNullOrEmpty(author) && !string.IsNullOrEmpty(author.Trim()))
                {
                    this.currentAuthor = author.Trim();
                }

                this.stateChanged = true;
                this.RaisePropertyChanged("CurrentAuthor");
            }
        }

        /// <summary>
        /// Gets or sets the save changes command.
        /// </summary>
        /// <value>The save changes command.</value>
        public DelegateCommand<SaveStateHandler> SaveChangesCommand { get; private set; }

        /// <summary>
        /// Gets or sets the cancel changes command.
        /// </summary>
        /// <value>The cancel changes command.</value>
        public DelegateCommand<object> CancelChangesCommand { get; private set; }

        /// <summary>
        /// Gets or sets the import command.
        /// </summary>
        /// <value>The import command.</value>
        public DelegateCommand<object> ImportCommand { get; private set; }

        /// <summary>
        /// Gets or sets the delete command.
        /// </summary>
        /// <value>The delete command.</value>
        public DelegateCommand<object> DeleteTypeInitializerCommand { get; private set; }

        #endregion Properties

        #region IRegistryManagerCenterPanePresenter Members

        /// <summary>
        /// Gets or sets the content view.
        /// </summary>
        /// <value>The content view.</value>
        public UIElement ContentView
        {
            get;
            set;
        }

        /// <summary>
        /// Gets or sets the refresh command.
        /// </summary>
        /// <value>The refresh command.</value>
        public DelegateCommand<BaseRegMgrModel> RefreshCommand { get; set; }

        /// <summary>
        /// Gets or sets the delete command.
        /// </summary>
        /// <value>The delete command.</value>
        public DelegateCommand<BaseRegMgrModel> DeleteCommand { get; set; }

        /// <summary>
        /// Gets or sets the update command.
        /// </summary>
        /// <value>The delete command.</value>
        public DelegateCommand<BaseRegMgrModel> UpdateCommand { get; set; }

        /// <summary>
        /// Gets or sets the select root command.
        /// </summary>
        /// <value>The select root command.</value>
        public DelegateCommand<object> SelectRootCommand { get; set; }

        /// <summary>
        /// Gets or sets the selected model.
        /// </summary>
        /// <value>The selected model.</value>
        public BaseRegMgrModel SelectedModel
        {
            get
            {
                return this.CurrentModel;
            }
            set
            {
                this.CurrentModel = value as TypeInitializerModel;
            }
        }

        /// <summary>
        /// Saves the changes.
        /// </summary>
        /// <returns>
        /// An enum value indicating the save status of the save operation, it can be Valid or Invalid.
        /// </returns>
        public SaveStateHandler SaveChanges()
        {
            return this.EnforceSaveChanges();
        }

        #endregion

        #region INotifyPropertyChanged Members

        /// <summary>
        /// Occurs when a property value changes.
        /// </summary>
        public event PropertyChangedEventHandler PropertyChanged;

        /// <summary>
        /// Raises the property changed.
        /// </summary>
        /// <param name="name">The name.</param>
        protected void RaisePropertyChanged(string name)
        {
            if (PropertyChanged != null)
            {
                this.PropertyChanged(this, new PropertyChangedEventArgs(name));
            }
        }

        #endregion

        #region Private Methods

        private void SaveChangesExecute(SaveStateHandler saveSuccess)
        {
            if (!this.CurrentModel.IsDeleted)
            {
                bool saveSucceeded = true;
                SR.TypeInitializer typeInitializer = null;
                StringBuilder errorMsg = new StringBuilder();
                try
                {
                    this.CurrentModel.Author = this.CurrentAuthor;
                    typeInitializer = this.registryManagerService.SaveTypeInitializer(this.CurrentModel);
                    if (null == typeInitializer)
                    {
                        saveSucceeded = false;
                    }
                }
                catch (TridentCustomException ex)
                {
                    errorMsg.Append(ex.Message);
                    saveSucceeded = false;
                }

                if (saveSucceeded)
                {
                    this.CurrentModel.Update(typeInitializer);
                    this.CurrentAuthor = this.CurrentModel.Author;
                    this.container.Resolve<IStatusMessageControl>().SetText(ManagementStudioResourceManager.GetString("TypeInitializerSaveSuccess"));
                }
                else
                {
                    TridentMessageBox.ShowTridentErrorMessageBox(errorMsg.ToString());
                }

                if (saveSuccess != null)
                {
                    saveSuccess.SaveOptionSelected = saveSucceeded ? SaveStatus.Valid : SaveStatus.Invalid;
                }

                this.stateChanged = !saveSucceeded;
            }
        }

        private void CancelChangesExecute()
        {
            this.Rollback();
            if (this.SelectRootCommand != null)
            {
                this.SelectRootCommand.Execute(null);
            }
        }

        private void Rollback()
        {
            if (this.CurrentModel != null)
            {
                this.CurrentAuthor = this.CurrentModel.Author;
                this.stateChanged = false;
            }
        }

        private void ImportCommandExecute()
        {
            // Get the top-most grid of the main window.
            Grid parentGrid = Application.Current.MainWindow.Content as Grid;

            SaveStateHandler saveStatus = this.EnforceSaveChanges();
            if (saveStatus.SaveOptionSelected == SaveStatus.Valid)
            {
                OpenFileDialog openDialog = new OpenFileDialog();

                // Filters the type dll in the current Directory.
                openDialog.Filter = "Custom type initializer assemblies |*.dll; *.exe";

                // Checks and promts the user if the file is not present in the directory.
                openDialog.CheckFileExists = true;

                // Check if File Exists in directory Specified.
                openDialog.CheckPathExists = true;

                // Restores the previous Directory the user had selected to Open/Save the file.
                openDialog.RestoreDirectory = true;

                // Window title.
                openDialog.Title = TridentResourceManager.GetString("ImportTypeInitializer");

                if ((bool)openDialog.ShowDialog())
                {
                    string message = TridentResourceManager.GetString("CustomTypeInitializerFailed");
                    StringBuilder strBuilder = new StringBuilder(message);
                    try
                    {
                        if (parentGrid != null)
                        {
                            // Create an instance of the wait control.
                            if (this.waitControl == null)
                            {
                                this.waitControl = new WaitControl(TridentResourceManager.GetString("Importing"));
                                Grid.SetRowSpan(this.waitControl, 3);
                            }

                            // Disable all existing children in this grid.
                            foreach (UIElement element in parentGrid.Children)
                            {
                                element.IsEnabled = false;
                            }

                            // Add the wait control.
                            parentGrid.Children.Add(this.waitControl);
                        }
                        else
                        {
                            // If parent grid cannot be retrieved, add a wait cursor.
                            Application.Current.MainWindow.Cursor = Cursors.Wait;
                        }
                        // Add the parameters to be sent to background worker.
                        Hashtable param = new Hashtable();
                        param.Add("FileName", openDialog.FileName);
                        // Create a background worker to upload the package.
                        BackgroundWorker worker = new BackgroundWorker();
                        worker.DoWork += new DoWorkEventHandler(this.OnImportCustomTypeInBackground);
                        worker.RunWorkerCompleted += new RunWorkerCompletedEventHandler(this.OnCustomTypeImported);
                        worker.RunWorkerAsync(param);
                    }
                    catch (TridentCustomException ex)
                    {
                        // If an error occured and the wait control could not be removed, force remove it now.
                        if (parentGrid != null && parentGrid.Children.Contains(this.waitControl))
                        {
                            // Enable all child elements.
                            foreach (UIElement element in parentGrid.Children)
                            {
                                element.IsEnabled = true;
                            }

                            // Add the wait control.
                            parentGrid.Children.Remove(this.waitControl);
                        }
                        strBuilder.Append(ex.Message);
                        message = strBuilder.ToString();
                        TridentMessageBox.ShowTridentErrorMessageBox(message);
                    }
                    catch (Exception ex)
                    {
                        // If an error occured and the wait control could not be removed, force remove it now.
                        if (parentGrid != null && parentGrid.Children.Contains(this.waitControl))
                        {
                            // Enable all child elements.
                            foreach (UIElement element in parentGrid.Children)
                            {
                                element.IsEnabled = true;
                            }

                            // Add the wait control.
                            parentGrid.Children.Remove(this.waitControl);
                        }

                        strBuilder.Append(ex.Message);
                        message = strBuilder.ToString();
                        TridentMessageBox.ShowTridentErrorMessageBox(message);
                    }
                    finally
                    {
                        // Set the cursor back to arrow.
                        Application.Current.MainWindow.Cursor = Cursors.Arrow;
                    }
                }
            }
        }

        /// <summary>
        /// Occurs after the cutom type has been uploaded.
        /// </summary>
        /// <param name="sender">
        /// Background worker thread.
        /// </param>
        /// <param name="e">
        /// Attributes of the event.
        /// </param>
        private void OnCustomTypeImported(object sender, RunWorkerCompletedEventArgs e)
        {
            // Get the top-most grid of the main window.
            Grid parentGrid = Application.Current.MainWindow.Content as Grid;
            if (parentGrid != null)
            {
                // Enable all child elements.
                foreach (UIElement element in parentGrid.Children)
                {
                    element.IsEnabled = true;
                }

                // Add the wait control.
                parentGrid.Children.Remove(this.waitControl);
            }

            Hashtable param = e.Result as Hashtable;
            if (param != null)
            {
                OperationStatus status = (OperationStatus)param["ImportStatus"];
                if (status == OperationStatus.Success)
                {
                    if (this.RefreshCommand != null)
                    {
                        this.RefreshCommand.Execute(this.CurrentModel);
                    }

                    this.container.Resolve<IStatusMessageControl>().SetText(ManagementStudioResourceManager.GetString("ImportCustomTypeInitializersSuccess"));
                }
                else if (status == OperationStatus.NoActionRequired)
                {
                    TridentMessageBox.ShowTridentErrorMessageBox(TridentResourceManager.GetString("AllAssembliesExist"));
                }
                else if (status == OperationStatus.Fail)
                {
                    TridentMessageBox.ShowTridentErrorMessageBox(Application.Current.MainWindow, TridentResourceManager.GetString("CustomTypeInitializerFailed"));
                }
            }
        }

        /// <summary>
        /// Occurs when the background worker thread starts its execution.
        /// </summary>
        /// <param name="sender">
        /// Background worker thread.
        /// </param>
        /// <param name="e">
        /// Attributes of the event.
        /// </param>
        private void OnImportCustomTypeInBackground(object sender, DoWorkEventArgs e)
        {
            Hashtable param = e.Argument as Hashtable;
            OperationStatus result = OperationStatus.Fail;
            if (param != null)
            {
                // Get all the required parameters.
                string filename = param["FileName"] as string;
                result = this.registryManagerService.ImportTypeInitializer(filename);

                //Store the result and exception message if caught in the param.
                param.Add("ImportStatus", result);

                // Store the param so that WorkerCompleted event can access it.
                e.Result = param;
            }
        }

        private void DeleteTypeInitializerCommandExecute()
        {
            MessageBoxResult res = TridentMessageBox.ShowTridentConfirmationYesNoMessageBox(ManagementStudioResourceManager.GetString("TypeInitializerDeleteConfirmationMsg"));
            if (res == MessageBoxResult.Yes)
            {
                try
                {
                    bool deleteSuccess = this.registryManagerService.DeleteTypeInitializer(this.CurrentModel);
                    if (deleteSuccess)
                    {
                        this.CurrentModel.IsDeleted = true;
                        if (this.DeleteCommand != null)
                        {
                            this.DeleteCommand.Execute(this.CurrentModel);
                        }
                    }
                    else
                    {
                        TridentMessageBox.ShowTridentErrorMessageBox(ManagementStudioResourceManager.GetString("TypeInitializerDeleteError"));
                    }
                }
                catch (TridentCustomException ex)
                {
                    TridentMessageBox.ShowTridentErrorMessageBox(ex.Message);
                }
            }
        }

        private SaveStateHandler EnforceSaveChanges()
        {
            SaveStateHandler saveState = new SaveStateHandler(SaveStatus.Valid);
            if (this.stateChanged && !this.CurrentModel.IsDeleted)
            {
                MessageBoxResult userResult = TridentMessageBox.ShowTridentConfirmationYesNoMessageBox(ManagementStudioResourceManager.GetString("TypeInitSaveChangesQuestion"));
                if (userResult == MessageBoxResult.Yes)
                {
                    this.SaveChangesCommand.Execute(saveState);
                }
                else if (userResult == MessageBoxResult.No)
                {
                    this.Rollback();
                }
            }

            return saveState;
        }

        #endregion Private Methods
    }
}
